#ifndef __MENSAJES_H_INCLUDED__
#define __MENSAJES_H_INCLUDED__

#include <string.h>
#include <iostream>
#include <list>
#include <map>
#include "Debug.h"
#include "Jugador.h"


#define MENSAJE_MAX_CONTENIDO    250
#define ID_SERVIDOR 0
#define MENSAJE_CONECTAR         0     //Mensaje que inicia la
//conexion. Como datos se manda el nombre del jugador conectado
//terminado en \0

#define MENSAJE_GET_JUGADORES    1     //Pide al programa servidor
//la lista de jugadores. No se envian datos.

#define MENSAJE_LISTA_JUGADORES  2     //Respuesta del servidor con
//la lista de jugadores (Hay mas). El formato de la lista es el
//siguiente: Primero se manda 1 byte indicando la cantidad de
//jugadores en la lista enviada. Luego se envian los jugadores en
//formato (nombre: string0, id: int, estado = char)

#define MENSAJE_LISTA_JUGADORES_FIN 3  //Respuesta del servidor con
//la lista de jugadores (No hay mas)

#define MENSAJE_INVITACION       4     //Mensaje que se envía a otro
//jugador para solicitar comenzar una partida. La seccion de datos
//contiene el id del jugador destinatario. Para aceptar la invitación,
//el receptor envía una invitación al otro jugador.

#define MENSAJE_DISPARO          5     //El jugador dispara al
//adversario. Como dato se envía 1 byte indicando coordenada x y 1
//byte indicando coordenada y siendo el primer casillero el (0,0)

#define MENSAJE_GET_TABLERO      6     //El jugador pide sus
//tableros. No lleva datos.

#define MENSAJE_TABLERO          7     //Contiene los tableros del
//jugador. Datos: los promeros 2 bytes representan las dimensiones del
//tablero (ancho, alto), luego, cada byte representa el estado de un
//casillero del tablero. Se envían 2 tableros juntos.

#define MENSAJE_DESCONECTAR      8     //Avisa que se termina el
//juego. Puede significar varias cosas: si el cliente se lo envía al
//servidor, quiere decir que se retira de la partida (si estaba
//jugando) o que termina el proceso (si no estaba jugando). Si lo
//envía el servidor al cliente significa que el otro jugador abandonó
//la partida (si estaba jugando).

#define MENSAJE_COMANDO          9     //Entrada de teclado del
				       //jugador.

#define MENSAJE_NOTIFICACION     10    //Mensaje de texto. El area de
//datos es un string.

#define MENSAJE_CEDER_TURNO      11    //Pasa el turno. Sin contenido

#define MENSAJE_ELEGIR_TURNO     12    //Elige un número que determina
//el jugador que comienza el juego

#define MENSAJE_GANAR            13
#define MENSAJE_PERDER           14

#define MENSAJE_COMENZAR_JUEGO   15    //Avisa a un jugador que
//comienza un juego con otro. La seccion de datos lleva la id
//del jugador rival y su nombre.

class ProcesoPrincipal;
class Mensaje{
private:
	long id_fuente;
	int tipo;
protected:
	char contenido[MENSAJE_MAX_CONTENIDO];

	void setTipo(int tipo){ this->tipo = tipo;}
	void borrar(){
		memset((void*)this, 0, sizeof(*this));
	}
	void setContenido(const char* cont, unsigned int offset=0){
		memcpy((void*)((char*)contenido+offset), (void*)cont, strlen(cont)+1);
	}
	std::string extraerCadena(unsigned int offset = 0){
		return std::string(contenido);
	}
public:
	int getTipo(){ return tipo;}
	Mensaje(){
		borrar();
	}

	void setFuente(long fuente){ id_fuente = fuente;}

	long getFuente(){ return id_fuente;}

	long size(){ return sizeof(Mensaje); }

	void* getBuffer(){ return this;}

	void mostrar(){
		std::dbg << "\n****************MENSAJE***************\n";
		std::dbg << "Origen:  " << id_fuente  << "\n";
		std::dbg << "Tipo:    " << tipo  << "\n";
		std::dbg << "Contenido: " << contenido << "\n";
		std::dbg << "**************************************\n";
	}
};


class MensajeConectar:public Mensaje{
public:
	MensajeConectar(std::string nombre){
		setTipo(MENSAJE_CONECTAR);
		setContenido(nombre.c_str());
	}

	std::string getNombre(){
		return extraerCadena().c_str();
	}
};

class MensajeGetJugadores:public Mensaje{
public:
	MensajeGetJugadores(){
		setTipo(MENSAJE_GET_JUGADORES);
	}
};

class MensajeListaJugadores:public Mensaje{
public:
	MensajeListaJugadores(){
		setTipo(MENSAJE_LISTA_JUGADORES);
	}

	void setUltimo(bool estado){
		if(estado)
			setTipo(MENSAJE_LISTA_JUGADORES_FIN);
		else
			setTipo(MENSAJE_LISTA_JUGADORES);
	}

	char getCantidad(){
		return contenido[0];
	}

	std::string getNombre(char index){
		if(index >= getCantidad())
			return "";
		return contenido+1;
	}

	int getID(char index){
		if(index >= getCantidad())
			return 0;
		return *(int*)(contenido+1+getNombre(0).size()+1);
	}

	char getJugando(char index){
		if(index >= getCantidad())
			return 0;
		return *(contenido+1+getNombre(0).size()+1+sizeof(int));
	}


	static std::list<MensajeListaJugadores> generarMensajes(long origen, int destino, ProcesoPrincipal *p);

	void procesar(std::map<int, std::string> &jugadores){
		char cantidad = contenido[0];
		int offset=1;
		while(cantidad > 0){
			std::string nombre=contenido+offset;
			int id = *(contenido+nombre.size()+1+offset);
			offset+=sizeof(id)+nombre.size()+1;
			jugadores[id] = nombre;
			cantidad--;
		}
	}
};

class MensajeInvitacion:public Mensaje{
public:
	MensajeInvitacion(int id_jugador){
		setTipo(MENSAJE_INVITACION);
		memcpy(contenido,(void*)&id_jugador, sizeof(id_jugador));
	}

	int getDestinatario(){ return *(int*)contenido;}
};

class MensajeDisparo:public Mensaje{
public:

	MensajeDisparo(char x, char y){
		setTipo(MENSAJE_DISPARO);
		setCoordenadas(x,y);
	}


	void setCoordenadas(char x,char y){
		contenido[0] = x;
		contenido[1] = y;
	}

	char getX(){
		return (char)contenido[0];
	}

	char getY(){
		return (char)contenido[1];
	}
};

class MensajeGetTablero:public Mensaje{
public:
	MensajeGetTablero(){
		setTipo(MENSAJE_GET_TABLERO);
	}
};

class MensajeTablero:public Mensaje{
public:
	MensajeTablero(){
		setTipo(MENSAJE_TABLERO);
	}

	void setDatos(Tablero &t1, Tablero &t2){
		int offset = 0;

		contenido[offset++]=(char)t1.getAncho();
		contenido[offset++]=(char)t1.getAlto();
		
		for(int j=0;j<t1.getAlto();j++)
			for(int i=0;i<t1.getAncho();i++)
				contenido[offset++] = (char)t1.getCasillero(i,j);

		contenido[offset++]=(char)t2.getAncho();
		contenido[offset++]=(char)t2.getAlto();
		
		for(int j=0;j<t2.getAlto();j++)
			for(int i=0;i<t2.getAncho();i++)
				contenido[offset++] = (char)t2.getCasillero(i,j);

	}

	void getDatos(Tablero &t1, Tablero &t2){
		int offset = 2;

		for(int j=0;j<t1.getAlto();j++)
			for(int i=0;i<t1.getAncho();i++)
				t1.setCasillero(i,j,contenido[offset++]);
		offset+=2;
		for(int j=0;j<t2.getAlto();j++)
			for(int i=0;i<t2.getAncho();i++)
				t2.setCasillero(i,j,contenido[offset++]);
	}

};

class MensajeDesconectar:public Mensaje{
public:
	MensajeDesconectar(){
		setTipo(MENSAJE_DESCONECTAR);
	}

};

class MensajeComando:public Mensaje{
public:
	MensajeComando(){
		setTipo(MENSAJE_COMANDO);
	}

	void setComando(std::string comando){ strcpy(contenido, comando.c_str());}
	std::string getComando(){ return contenido; }

};

class MensajeCederTurno:public Mensaje{
public:
	MensajeCederTurno(){
		setTipo(MENSAJE_CEDER_TURNO);
	}
};

class MensajeElegirTurno:public Mensaje{
	int numero;
public:
	MensajeElegirTurno(int numero):numero(numero){
		setTipo(MENSAJE_ELEGIR_TURNO);
	}
	
	int getNumero(){ return numero;}
};

class MensajeNotificacion:public Mensaje{
public:
	MensajeNotificacion(std::string mensaje=""){
		setTipo(MENSAJE_NOTIFICACION);
		setMensaje(mensaje);
	}

	void setMensaje(std::string mensaje){
		strcpy(contenido, mensaje.c_str());
	}
	
	std::string getMensaje(){
		return contenido;
	}
};

class MensajeGanar:public Mensaje{
public:
	MensajeGanar(){
		setTipo(MENSAJE_GANAR);
	}
};

class MensajePerder:public Mensaje{
public:
	MensajePerder(){
		setTipo(MENSAJE_PERDER);
	}
};

class MensajeComenzarJuego:public Mensaje{
public:
	MensajeComenzarJuego(int id, std::string nombre){
		setTipo(MENSAJE_COMENZAR_JUEGO);
		*(int*)contenido = id;
		strcpy(contenido+sizeof(int), nombre.c_str());
	}

	int getId(){
		return *(int*)contenido;
	}

	std::string getNombre(){
		return contenido+sizeof(int);
	}
};
#endif /* __MENSAJES_H_INCLUDED__ */
